추상화 기법

  • 추상화는 도메인의 복잡성을 단순화하고 직관적인 멘탈 모델을 만드는 데 사용할 수 있는 가장 기본적인 인지 수단입니다.
  • 사람들은 도메인에 존재하는 개념들을 구조화하고 단순화하기 위해 다양한 추상화 기법을 사용합니다.
  • 특성을 공유하는 객체들을 동일한 타입으로 분류하는 것은 객체지향 패러다임에서 사용하는 추상화 기법의 한 예입니다.

추상화 기법

  • 객체지향의 가장 큰 장점은 동일한 추상화 기법을 프로그램의 분석, 설계, 구현 단계에 걸쳐 일관성 있게 적용할 수 있다는 점입니다.
  • 추상화 기법의 종류
    • 분류와 인스턴스화 : 분류는 객체의 구체적인 세부 사항을 숨기고 인스턴스 간에 공유하는 공통적인 특성을 기반으로 범주를 형성하는 과정입니다. 분류의 역은 범주로부터 객체를 생성하는 인스턴스화 과정입니다.
    • 일반화와 특수화 : 일반화는 범주 사이의 차이를 숨기고 범주 간에 공유하는 공통적인 특성을 강조합니다. 일반화의 역을 특수화라고 합니다.
    • 집합과 분해 : 집합은 부분과 관련된 세부 사항을 숨기고 부분을 사용해서 전체를 형성하는 과정을 가리킵니다. 집합의 반대 과정은 전체를 부분으로 분리하는 분해 과정입니다.

분류와 인스턴스화

개념과 범주

  • 객체를 분류하고 범주로 묶는 것은 객체들의 특정 집합에 공통의 개념을 적용하느 것을 의미합니다. 개념이란 속성과 행위가 유사한 객체에 공통저긍로 적용되는 관념이나 아이디어 입니다.
  • 세상에 존재하는 객체에 개념을 적용하는 과정을 분류라고 합니다. 분류는 객체를 특정한 개념을 나타내는 집합의 구성 요소로 포함시킵니다.
  • ‘수 많은 개별적인 현상들’을 객체라고 하고, ‘하나의 개념’을 타입이라고 합니다. 분류는 객체를 타입과 연관시키는 것입니다. 분류의 역은 타입에 해당하는 객체를 생성하는 과정으로 인스턴스화 또는 예시라고 합니다.
  • 객체지향의 세계에서 개념을 가리키는 표준 용어는 타입입니다. 따라서 타입은 개념과 동의어이며 속성과 행위가 유사한 객체에 공통적으로 적용되는 관념이나 아이디어를 의미합니다. 이런 관점에서 분류란 객체들을 동일한 타입 도는 범주로 묶는 과정을 의미하므로 객체를 타입의 인스턴스라고 합니다.
  • 분류는 객체와 타입 간의 관계를 나타낸 것입니다. 어떤 객체가 타입의 정의에 부합할 경우 그 객체는 해당 타입으로 분류되며 자동으로 타입의 인스턴스가 됩니다.

타입

  • 객체를 타입에 따라 분류하기 위해서는 객체가 타입에 속하는지 여부를 확인할 수 있어야 합니다.
  • 타입을 객체의 분류 장치로서 적용할 수 있으려면 다음과 같은 세 가지 관점에서의 정의가 필요합니다.
    • 심볼 : 타입을 가리키는 간략한 이름이나 명칭
    • 내연 : 타입의 완전한 정의. 내연의 의미를 이용해 객체가 타입에 속하는지 여부를 확인할 수 있습니다.
    • 외연 : 타입에 속하는 모든 객체들의 집합

외연과 집합

  • 타입의 외연은 타입에 속하는 객체들의 집합으로 표현합니다.
  • 집합은 외연을 가리키는 또 다른 명칭입니다. 객체들은 동시에 서로 다른 집합에 포함될 수도 있습니다.
  • 단일 분류(single classification)와 다중 분류(multiple classification)이 있는데 대부분의 객체지향 프로그래밍 언어들은 단일 분류만을 지원합니다.
    • 한 객체는 오직 한 클래스의 인스턴스여야만 하며 동시에 두 개의 클래스의 인스턴스일 수 없습니다.
    • 다중 상속과 다중 분류를 혼동해서는 안 됩니다. 다중 상속은 하나의 타입이 다수의 슈퍼타입을 가질 수 있도록 허용하지만 타입 정의를 생략할 수는 없습니다. 반면 다중 분류는 특정한 타입을 정의하지 않고도 하나의 객체가 서로 다른 타입의 인스턴스가 되도록 허용합니다.
  • 객체를 특정한 타입으로 분류하면 해당 객체는 타입의 집합에 포합됩니다.
  • 객체가 한 집합에서 다른집합의 원소로 자신이 속하는 타입을 변경할 수 있는 경우 이를 동적 분류(dynamic classification)이라고 합니다.
  • 객체가 자신의 타입을 변경할 수 없는 경우 이를 정적 분류(static classification)라고 합니다.
  • 다중 분류와 동적 분류는 서로 배타적인 개념이 압니다. 개념적인 관점에서 다중 분류와 동적 분류를 함께 적용하는 것이 실세계의 복잡성을 모델링하는 데 유용합니다. 대부분의 언어는 정적 분류만 허용하며 동적 분류를 구현할 수 있는 방법을 제공하지 않습니다.
  • 다중 분류와 동적 분류는 개념적인 관점에서 도메인을 분석하는 데는 유용하지만 객체지향 프로그래밍 언어의 제약으로 인해 이를 구현으로 옮기는 것은 쉽지 않습니다.
  • 다중 분류와 동적 분류 관점에서 도메인 모델의 초안을 만든 후 실제 구현에 접합하도록 단일 분류와 정적 분류 바식으로 객체들의 범주를 재조정하는 편이 분석과 구현 간의 차이를 메울 수 있는 가장 현실적인 방법입니다.
  • 마틴 파울러는 다중 분류와 동적 분류를 구현할 수 있는 다양한 방식의 ‘디자인 템플릿’을 소개하고 있습니다. 이러한 디자인 템플릿은 유연성이라는 측면에서 반드시 필요한 경우에만 사용해야 합니다. 단순함을 위해서는 항상 다중 분류와 동적 분류보다는 단일 분류와 정적 분류를 선택하는 것이 현명합니다.

클래스

  • 클래스는 타입을 재사용하는 용도 외에도 코드를 잿용하는 용도로 사용되기도 합니다.
  • 햔재 객체지향 패러다임은 아리스토텔레스의 분류법의 근간을 형성하는 아이디어를 기반으로 합니다. 만약 객체들이 동일한 특성을 가진다면 그것들은 동일한 카테고리에 속합니다.
    • 아리스토텔레스는 객체의 특성을 본질적인 속성과 우연적인 속성으로 분류했습니다. 본질이란 한 사물의 가장 핵심적이고 필수불가결한 속성입니다. 본질적이지 않은 속성을 우연적 속성이라고 합니다.
    • 자바스크립트처럼 클래스가 존재하지 않는 프로토타입 기반의 언어는 아리스토텔레스의 객관적인 분류 체계가 존재한다는 사상에 대한 철학적 의문에 그 뿌리를 두고 있습니다. 클래스가 없는 프로토타입 언어에서 분류와 인스턴스화는 프로토타입이라는 객체의 복사를 통해 이뤄집니다.

일반화와 특수화

범주와 계층

  • 린네의 분류 체계는 범주 간의 계층적인 구조를 가집니다. 린네의 계층 구조는 좀 더 세부적인 범주가 계층의 하위에 위치하고 좀 더 일반적인 범주가 계층의 상위에 위치합니다. 이때 계층의 상위에 위치한 범주를 계층의 하위에 위치한 범주의 일반화라고 하고, 계층의 하위에 위치한 범주는 계층의 상위의 위치한범주의 특수화라고 합니다.

서브타입

  • 객체지향의 세계에서 범주는 개념을 의미하고, 개념은 타입을 의미하므로 일반화와 특수화는 계층 구조 안에 존재하는 타입 간의 관계를 의미합니다. 따라서 좀 더 일반적인 타입을 이용해 좀 더 세부적인 타입을 정의함으로써 타입 간의 계층 구조를 구축할 수 있습니다.
  • 슈퍼타입은 서브타입의 일반화이고 서브타입은 슈퍼타입의 특수화입니다.
    • 어떤 타입이 다른 타입보다 일반적이라면 이 타입을 슈퍼 타입이라고 합니다.
    • 어떤 타입이 다른 타입보다 좀 더 특수하다면 이 타입을 서브타입이라고 합니다.
  • 일반화와 특수화의 계층 구조에서 서브타입은 슈퍼티입이 가진 본질적인 속성과 함께 자신만의 추가적인 속성을 가집니다. 이것은 내연 관점에서 슈퍼타입의 정의보다 더 일반적이라는 것을 의미합니다.
  • 서브타입은 슈퍼타입의 본질적인 속성을 모두 포함하기 때문에 계층에 속하는 모든 서브타입들이 슈퍼타입의 속성을 공유한다는 것을 쉽게 예상할 수 있습니다. 이 같은 일반화의 특징을 이용하면 부분적인 사실을 통해 복잡한 사실에 대해 논리적인 추론이 가능해집니다.
  • 객체의 집합을 나타내는 외연의 관점에서 서브타입은 슈퍼타입의 부분집합으로 표현됩니다.
  • 크레이그 라만은 어떤 타입이 다른 타입의 서브타입이 되기 위해서는 ‘100% 규칙’과 ‘Is-a 규칙’을 준수해야 한다고 말합니다.
    • 100% 규칙은 타입의 내연과 관련된 규칙이며, Is-a 규칙은 타입의 외연과 관련된 규칙입니다. 두 타입이 100% 규칙과 Is-a 규칙을 만족시키지 못할 경우 두 타입 간에 일반화 거ㅘㄴ계는 성립하지 않습니다.
    • 100% 규칙 : 슈퍼타입의 정의가 100% 서브타입에 적용돼야만 합니다. 서브타입은 속성과 연관관계 면에서 슈퍼타입과 100% 일치해야 합니다.
    • Is-a 규칙 : 서브타입의 모든 인스턴스는 슈퍼타입 집합에 포함돼야 합니다. 이는 대개 영어로 서브타입은 슈포타입입니다라는 구문을 만듦으로써 테스트할 수 있습니다.
    • 일반화 관계를 is-a 관계라고 합니다. is-a 관계의 본질은 서브타입 슈퍼타입의 부분집합이라는 것입니다.

상속

  • 프로그래밍 언어를 이용해 일반화와 특수화 관꼐를 구현하는 가장 일반적인 방법은 클래스 간의 상속을 사용하는 것입니다. 그러나 안타깝게도 모든 상속 관계가 일반화 관계인 것은 아닙니다.
  • 일반화의 원칙인 한 타입이 다른 타입의 서브타입이 되기 위해서는 슈퍼타입에 순응해야 한다는 것입니다. 순응에는 구조적인 순응과 행위적인 순응의 두 가지 종류가 있습니다. 두 가지 모두 특정 기대 집합에 대해 서브타입이 슈펕입에 대한 대체 가능성을 의미합니다. 구조적인 순으으이 경우 기대 집합은 속성과 연관관계에 관한 것이며, 행위적인 순응의 경우 기대 집은 행위가 동일한 계약을 깁나으로 하느냐에 관한 것입니다.
    • 구조적인 순응은 타입의 내연과 관련된 100% 규칙을 의미합니다.
    • 행위적인 순응은 타입의 행위에 관한 것이며, 서브타입은 슈퍼타입을 행위적으로 대체 가능해야 합니다. 행위적인 순응을 흔히 리스코프 치환 원칙이라고 합니다.
  • 상속은 코드 재사용하기 위한 언어적 메커니즘을 제공하는 것으로서 상속이 대체 가능성을 만든다는 것을 보장하지 않습니다.
  • 상속은 서브타이핑과 서브클래싱의 두 가지 용도로 사용될 수 있습니다.
    • 서브클래스가 슈퍼클래스를 대체할 수 있는 경우 이를 서브타입핑이라고 합니다. 서브타이핑은 설계의 유연성이 목표입니다. 서브타이핑을 인터페이스 상속이라고 합니다.
    • 서브클래스가 슈퍼클래스를 대체할 수 없는 경우에는 서브클래싱이라고 합니다. 서프클래싱은 코드의 중복 제거와 재사용이 목적입니다. 서브 클래싱을 구현 상속이라고 합니다.
    • 서브 타이핑의 전제 조건은 대체 가능성이기 때문에 서브타피이인지 여부를 확인하려면 클라이언트 관점에서 실제로 어떻게 사용되고 있는지를 확인해야 합니다.
  • 일반화를 위한 서브타이핑은 특정 기대 집합에 대한 서브타입과 슈퍼타입 간의 구조적, 또는 행위적 순응 관계를 의미하며, 대체 가능성을 내포합니다. 상속은 서브 타이핑을 프로그래밍 언어적으로 구현하는 데 사용될 수 있지만 모든 상속이 서브타이핑인 것은 아닙니다. 서브클래스가 슈퍼클래스를 대체할 수 없고 단지 코드만 공유하고 있다면 서브타이핑이 아니라 서브클래싱이라고 합니다. 가능한 모든 산송 관계가 서브타이핑의 대체 가능성을 준수하도록 주의 깊에 사용하는 것은 코드를 유연하게 만들고 재사용성을 높이는 한 가지 방법입니다.
  • 여러 클래스로 구성된 상속 계층에서 수신된 메시지를 이해하는 기본적인 방법은 클래스 간의 위임(Delegation)을 사용하는 것입니다. 어떤 객체의 클래스가 수신된 메시지를 이해할 수 없다면 메시지를 클래스의 부모 클래스로 위임합니다. 클래스 간의 위임 사슬은 계층 내의 어떤 클래스가 메시지를 처리하거나 최상위 부모 클래스에 위임 될 때까지 계속됩니다.

집합과 분해

계층적인 복잡성

  • 복잡성은 ‘계층’의 형태를 띕니다.
  • 단순한 형태로부터 복잡한 형태로 진화하는 데 걸리는 시간은 그 사이에 존재하는 ‘안정적인 형태’의 수와 분포에 의존합니다.
  • 안정적인 형태의 부분으로부터 전체를 구축하는 행위를 집합이라고 하고 집합과 반대로 전체를 부분으로 분할하는 행위를 분해라고 합니다.
    • 집합의 가치는 많은 수의 불필요한 세부 사항을 배제하고 큰 그림에서 대상을 다룰 수 있게 합니다. 그러나 필요한 시점에는 전체를 분해함으로써 그 안에 포함된 부분들을 새로운 전체로 다룰 수 있습니다. 전체와 부분 간의 일관된 계층 구조는 재귀적인 설꼐를 가능하게 합니다.
    • 집합은 전체의 내부로 불필요한 세부 사항을 감춰주기 때문에 추상화 메커니즘인 동시에 캡슐화 메커니즘 입니다. 외부에서는 전체에 관해서만 알고 있고 내부의 세부 사항에 대해서는 알지 못하기 때문에 내부의 구성을 변경하더라도 외부에 영향을 미치지 않습니다.
    • 집합과 분해는 한 번에 다뤄야 하는 요소의 수를 감소시킴으로써 인지 과부하를 방지합니다.

합성 관계

  • 객체와 객체 사이의 전체-부분 관계를 구현하기 위해서는 합성 관계를 사용합니다.
  • 합성 관계는 부분을 전체 안에 캡슐화함으로써 인지 과부하를 방지합니다. 객체들의 그룹관 관련된 복잡성이 완화 됩니다.
  • 합성 관계와 연관 관계 사이의 차이가 항상 명확한 것은 아니지만 일반적으로 합성 관계로 연결된 객체는 포함하는 객체가 제거될 대 내부에 포함된 객체도 함께 제거됩니다. 합성 관계는 생명주기 측면에서 연관 관계보다 더 강하게 객체들을 결합합니다.

패키지

  • 관련된 클래스 집합을 하나의 논리적인 단위로 묶는 구성 요소를 패키지 또는 모듈이라고 합니다.
  • 패키지를 이용하면 시스템의 전체적인 구조를 이해하기 위해 한 번에 고려해야 하는 요소의 수를 줄일 수 있습니다. 또한 개별 클래스가 아닌 클래스의 집합을 캡슐화함으로써 전체적인 복잡도를 낮출 수 있습니다. 함께 협력하는 응집도 높은 클래스 집합을 하나의 패키지 내부로 모으면 코드를 이해하기 위해 패키지 경계를 넘나들 필요가 적어집니다.
  • 합성 관계가 내부에 포함된 객체들의 존재를 감춤으로써 내부 구조를 추상화하는 것처럼 패키지 내부에 포함된 클래스들을 감춤으로써 시스템의 구조를 추상화합니다.

참고